package xyz.archknight.app.loginput.dictionary.gen.ui.model

import com.hankcs.hanlp.HanLP
import javax.swing.table.AbstractTableModel

/**
 * 通用词汇表数据控制模型
 *
 * @author midnite
 * create at 2020-06-19 4:19 下午
 */
data class WordTableRow(
    val id: Int,
    val phase: String,
    val pinyin: String,
    val dirty: Boolean,
    val new: Boolean,
    val removed: Boolean
) {
    operator fun get(index: Int): Any =
        arrayOf(this.phase, this.pinyin, this.id, this.dirty, this.new, this.removed)[index]

    operator fun contains(word: String): Boolean = this.phase == word
}

class WordTableModel : AbstractTableModel(), Iterable<WordTableRow> {
    private val columnNames = arrayOf("自定义词组", "拼音串")
    private val data: MutableList<WordTableRow> = mutableListOf()

    operator fun get(row: Int): WordTableRow = data[row]

    override fun getRowCount(): Int = data.size

    override fun getColumnCount(): Int = columnNames.size

    override fun getValueAt(rowIndex: Int, columnIndex: Int): Any = data[rowIndex][columnIndex]

    override fun setValueAt(aValue: Any?, rowIndex: Int, columnIndex: Int) {
        when(columnIndex) {
            0 -> data[rowIndex] = data[rowIndex].copy(phase = aValue.toString(), dirty = true)
            1 -> data[rowIndex] = data[rowIndex].copy(pinyin = aValue.toString(), dirty = true)
            else -> { }
        }
        fireTableCellUpdated(rowIndex, columnIndex)
    }

    override fun isCellEditable(row: Int, column: Int): Boolean = true

    override fun getColumnName(column: Int): String = columnNames[column]

    fun reset() {
        data.clear()
        fireTableDataChanged()
    }

    fun addNewRow(phase: String = "", pinyin: String= "", insertEvent: Boolean = false) {
        if (data.map { it.phase }.contains(phase)) {
            return
        }
        data.add(WordTableRow(0, phase, pinyin, dirty = true, new = true, removed = false))
        if (insertEvent) {
            fireTableRowsInserted(data.size - 1, data.size - 1)
        } else {
            fireTableDataChanged()
        }
    }

    fun load(id: Int, word: String, pinyin: String) {
        data.add(WordTableRow(id, word, pinyin, dirty = false, new = false, removed = false))
        fireTableDataChanged()
    }

    fun markDirty(row: Int) {
        data[row] = data[row].copy(dirty = true)
        fireTableDataChanged()
    }

    fun update(row: Int, word: String, pinyin: String, dirty: Boolean = true) {
        if (data.filterIndexed { index, _ -> index != row }
                .map { it.phase }
                .contains(word)) {
            if (data[row].new) {
                data.removeAt(row)
            } else {
                data.removeIf { it.new && it.phase == word }
            }
        } else {
            data[row] = data[row].copy(phase = word, pinyin = pinyin, dirty = dirty)
        }
        fireTableDataChanged()
    }

    fun remove(row: Int) {
        data[row] = data[row].copy(removed = true)
        fireTableDataChanged()
    }

    fun removeById(id: Int) {
        val row = data.indexOfFirst { it.id == id }
        remove(row)
    }

    fun removeNewByPhase(phase: String) {
        data.removeIf { it.phase == phase && it.new }
        fireTableDataChanged()
    }

    fun removeByRowOnly(row: Int) {
        data.removeAt(row)
        fireTableDataChanged()
    }

    fun exists(phase: String): Boolean = data.any { phase in it }

    fun isDirtyRow(row: Int): Boolean = data[row].dirty

    fun isNewLine(row: Int): Boolean = data[row].new

    fun hasDirty(): Boolean = data.any { it.dirty || it.removed }

    fun markAllClean() {
        for ((index, element) in data.withIndex()) {
            data[index] = element.copy(dirty = false, removed = false)
        }
    }

    override fun iterator(): Iterator<WordTableRow> = data.iterator()

    fun merge(rows: IntArray) {
        val newWord = rows.joinToString(separator = "") { data[it].phase }
        val pinyin = HanLP.convertToPinyinString(newWord, "'", false)
        data[rows[0]] = data[rows[0]].copy(phase = newWord, pinyin = pinyin, dirty = true)
        rows.slice(1 until rows.size).forEach { remove(it) }
        fireTableDataChanged()
    }
}